package service

import (
	"fmt"
	"log"
	"time"

	"github.com/go-redis/redis"
	"github.com/go-xorm/xorm"

	"im/common"
	"im/dao"
	"im/errcode"
	"im/model"
	"im/requests"
	"im/service/cache"
	"im/service/orm"
)

type UsersFriends struct {
	db    *xorm.Engine
	cache *redis.Client
}

const EACH_OTHER_FRIENDS_IS_TRUE = 1
const EACH_OTHER_FRIENDS_IS_FALSE = 0
const EACH_OTHER_FRIENDS_IS_NIL = -1

func NewUsersFriends() *UsersFriends {
	return &UsersFriends{db: orm.GetDB()}
}

// 获取关注我的人
func (s *UsersFriends) ListFollowers(userId int64, pagan requests.Pagination) ([]model.UsersVisually, error) {
	var friendsList []struct {
		model.UsersFriends
		model.Users
	}
	var friendInfos []model.UsersVisually
	err := s.db.Table(model.UsersFriends{}).Join("inner", "users", "users.id=users_friends.user_id").
		Where("friend_id=?", userId).Where("type=?", model.UsersFriendsTypeGood).
		Limit(pagan.Limit, pagan.GetOffset()).
		Find(&friendsList)
	if err != nil {
		return friendInfos, errcode.NewError(errcode.DaoFailed, err)
	}
	for _, row := range friendsList {
		friendInfos = append(friendInfos, row.UsersVisually)
	}
	return friendInfos, nil
}

// 获取我关注的人
func (s *UsersFriends) ListFocus(userId int64, pagan requests.Pagination) ([]model.UsersVisually, error) {
	var friendsList []struct {
		model.UsersFriends
		model.Users
	}
	var friendInfos []model.UsersVisually
	err := s.db.Table(model.UsersFriends{}).Join("inner", "users", "users.id=users_friends.friend_id").
		Where("user_id=?", userId).
		Where("type=?", model.UsersFriendsTypeGood).
		Limit(pagan.Limit, pagan.GetOffset()).Find(&friendsList)
	if err != nil {
		return friendInfos, errcode.NewError(errcode.DaoFailed, err)
	}
	for _, row := range friendsList {
		friendInfos = append(friendInfos, row.UsersVisually)
	}
	return friendInfos, nil
}

func (s *UsersFriends) Focus(user model.Users, param requests.PostFriends) error {
	var row = model.UsersFriends{}
	exist, err := s.db.Table(model.UsersFriends{}).Where("user_id=?", user.Id).Where("friend_id=?", param.FriendId).
		Where("type=?", model.UsersFriendsTypeGood).Get(&row)
	if err != nil {
		return errcode.NewError(errcode.DaoFailed, err)
	}
	if exist {
		return nil
	}
	_, err = s.db.InsertOne(model.UsersFriends{
		AppsId:   user.AppsId,
		UserId:   user.Id,
		FriendId: param.FriendId,
		Type:     model.UsersFriendsTypeGood,
	})
	if err != nil {
		return errcode.NewError(errcode.DaoFailed, err)
	}
	dao.NewUsersDao().Incr(user.Id, "focus")
	dao.NewUsersDao().Incr(param.FriendId, "followers")
	return nil
}
func (s *UsersFriends) UnFocus(userId int64, friendId int64) error {
	count, err := s.db.Where("user_id=?", userId).Where("friend_id=?", friendId).
		Where("type=?", model.UsersFriendsTypeGood).Delete(model.UsersFriends{})
	if err != nil {
		return errcode.NewError(errcode.DaoFailed, err)
	}
	if count <= 0 {
		return nil
	}
	dao.NewUsersDao().Decr(userId, "focus")
	dao.NewUsersDao().Decr(friendId, "followers")
	return nil
}
func (s *UsersFriends) ToMap(input []model.UsersFriends, key string) map[int64]interface{} {
	var friendMap = make(map[int64]interface{}, 0)
	if input == nil || len(input) == 0 {
		return friendMap
	}
	for _, row := range input {
		switch key {
		case "userId":
			friendMap[row.UserId] = nil
		case "friendId":
			friendMap[row.FriendId] = nil
		}
	}
	return friendMap
}
func (s *UsersFriends) GetFriendStatus(userId int64, friendIds ...int64) (map[int64]int, error) {
	var (
		err          error
		followers    []model.UsersFriends
		followersMap map[int64]interface{}
		focuses      []model.UsersFriends
		focusesMap   map[int64]interface{}
		friendMap    map[int64]int
	)

	err = s.db.Where("friend_id=?", userId).
		In("user_id", common.ToInterfaceSlice(friendIds)...).Find(&followers)
	if err != nil {
		return friendMap, errcode.NewError(errcode.DaoFailed, err)
	}
	followersMap = s.ToMap(followers, "userId")

	err = s.db.In("friend_id", common.ToInterfaceSlice(friendIds)).
		Where("user_id=?", userId).Find(&focuses)
	if err != nil {
		return friendMap, errcode.NewError(errcode.DaoFailed, err)
	}
	focusesMap = s.ToMap(focuses, "friendId")
	friendMap = make(map[int64]int, 0)
	for _, friendId := range friendIds {
		var status = 0
		if _, exist := followersMap[friendId]; exist {
			status = status | model.UserFriendMyFollower
		}
		if _, exist := focusesMap[friendId]; exist {
			status = status | model.UserFriendMyFocuser
		}
		friendMap[friendId] = status
	}
	return friendMap, nil
}

// 判断是不是互为好友
// Deprecated: 不用判断是不是好友关系
func (s *UsersFriends) EachOtherFriends(appId uint, userA int64, userB int64) (result int, err error) {

	defer func() {
		if err == nil {
			err = s.SetCacheOfEOF(appId, userA, userB, result)
		}
	}()

	// 获取缓存中好友状态
	result, err = s.cache.Get(s.getCacheKeyOfEOF(appId, userA, userB)).Int()
	if err == nil {
		return result, nil
	}

	uu1 := new(model.UsersFriends)
	ok, err := s.db.Where("apps_id=?", appId).Where("user_id=?", userA).Where("friend_id=?", userB).Get(uu1)
	if err != nil {
		return EACH_OTHER_FRIENDS_IS_NIL, err
	}
	if !ok {
		return EACH_OTHER_FRIENDS_IS_FALSE, nil
	}
	uu2 := new(model.UsersFriends)
	ok, err = s.db.Where("apps_id=?", appId).Where("user_id=?", userB).Where("friend_id=?", userA).Get(uu2)
	if err != nil {
		return EACH_OTHER_FRIENDS_IS_NIL, err
	}
	if !ok {
		log.Println(appId, userB, userA, uu2)
		return EACH_OTHER_FRIENDS_IS_FALSE, nil
	}
	return EACH_OTHER_FRIENDS_IS_TRUE, nil

}

// Deprecated: 不用判断是不是好友关系
func (s *UsersFriends) getCacheKeyOfEOF(appId uint, userA int64, userB int64) string {
	if userA > userB {
		return fmt.Sprintf("%d_%d_%d_%d", cache.EACH_OTHER_FRIENDS, appId, userB, userA)
	}
	return fmt.Sprintf("%d_%d_%d_%d", cache.EACH_OTHER_FRIENDS, appId, userA, userB)
}

// Deprecated: 不用判断是不是好友关系
func (s *UsersFriends) SetCacheOfEOF(appId uint, userA int64, userB int64, handshake int) error {
	return s.cache.Set(s.getCacheKeyOfEOF(appId, userA, userB), handshake, time.Hour*24).Err()
}

// Deprecated: 不用判断是不是好友关系
func (s *UsersFriends) DelCacheOfEOF(appId uint, userA int64, userB int64) error {
	return s.cache.Del(s.getCacheKeyOfEOF(appId, userA, userB)).Err()
}
